package dataaccess;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import entity.annotation.Id;
import entity.annotation.Manytoone;
import entity.annotation.Onetomany;
import entity.annotation.Table;
import util.Connect;

public class DataImport<T> {
	private Class<T> tclass;
	private String tablename;
	private Connection con;

	@SuppressWarnings("unchecked")
	public DataImport() {
		ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
		tclass = (Class<T>) pt.getActualTypeArguments()[0];
		con = Connect.getConnection();

		Table tb = tclass.getAnnotation(Table.class);
		if (tb != null)
			tablename = tb.name();
		else
			tablename = tclass.getSimpleName();
	}

	public List<T> getAll() {
		List<T> list = new ArrayList<>();
		StringBuffer sql = new StringBuffer("select * from ").append(tablename);
		try {
			Statement stm = con.createStatement();
			ResultSet rs = stm.executeQuery(sql.toString());
			Field[] fd = tclass.getDeclaredFields();
			while (rs.next()) {
				T o = tclass.newInstance();
				for (int i = 0; i < fd.length; i++) {
					fd[i].setAccessible(true);
					Manytoone mte = fd[i].getAnnotation(Manytoone.class);
					Onetomany otm = fd[i].getAnnotation(Onetomany.class);
					Class type = fd[i].getType();
					String typename = type.getSimpleName();
					Object val = null;
					if (mte == null && otm == null)
						val = rs.getObject(fd[i].getName());
					else if (mte!=null) {
						String maptab = mte.mapby();
						String sql2 = "select * from " + maptab + " where " + mte.id() + "=" + rs.getObject(mte.fk());						
						Statement st=con.createStatement();
						ResultSet rs1 = st.executeQuery(sql2);
						rs1.next();
						Object ro = type.newInstance();
						Field[] rfd = type.getDeclaredFields();
						for (int j = 0; j < rfd.length; j++) {
							rfd[j].setAccessible(true);
							Object rval = rs1.getObject(rfd[j].getName());
							if (rfd[j].getType().getSimpleName().equals("Integer")) {
								BigDecimal bd = (BigDecimal) rval;
								rval = bd.intValue();
							}
							rfd[j].set(ro, rval);
						}
						val = ro;
					}
					if(otm!=null){
						String maptab=otm.mapby();
						String sql3 = "select * from "+maptab+" where "+otm.id()+"="+rs.getObject(otm.fk());
						Statement st=con.createStatement();
						ResultSet rs2=st.executeQuery(sql3);
						List<Object> stus=new ArrayList<>();
						ParameterizedType pt=(ParameterizedType) fd[i].getGenericType();
						Class typ=(Class) pt.getActualTypeArguments()[0];

						while(rs2.next()){
							Object ro = typ.newInstance();
							Field[] rfd = typ.getDeclaredFields();
							for (int j = 0; j < rfd.length; j++) {
								rfd[j].setAccessible(true);
								Object rval=null;
								if(rfd[j].getAnnotation(Manytoone.class)==null&&rfd[j].getAnnotation(Onetomany.class)==null)
									{
										rval = rs2.getObject(rfd[j].getName());
									}
								if (rfd[j].getType().getSimpleName().equals("Integer")) {
									BigDecimal bd = (BigDecimal) rval;
									rval = bd.intValue();
								}
								rfd[j].set(ro, rval);
							}
							stus.add(ro);
						}
						val=stus;
					}
					if (typename.equals("Integer")) {
						if (val == null)
							val = 0;
						else {
							BigDecimal bd = (BigDecimal) val;
							val = bd.intValue();
						}
					}
					fd[i].set(o, val);
				}
				System.out.println("=======");
				list.add(o);
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return list;
	}
	// insert into tablename values(?,?,?,...)
	// delete from tablename where id=?
	// update tablename set fd1=?,fd2=?,fd3=?,... where id=?

	public void delete(T o) {
		int idindex = 0;
		StringBuffer sql = new StringBuffer("delete from ").append(tablename).append(" where ");
		Field[] fd = tclass.getDeclaredFields();
		for (int i = 0; i < fd.length; i++) {
			fd[i].setAccessible(true);
			Id id = fd[i].getAnnotation(Id.class);
			if (id != null) {
				idindex = i;
			}
		}

		String fieldname = fd[idindex].getName();
		sql.append(fieldname + "=?");
		try {
			PreparedStatement psm = con.prepareStatement(sql.toString());
			psm.setObject(1, fd[idindex].get(o));
			// System.out.println(sql);
			psm.executeUpdate();
		} catch (SQLException | IllegalArgumentException | IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public void update(T o) {
		int index = 0;
		StringBuffer sql = new StringBuffer("update ").append(tablename).append(" set ");
		Field[] fd = tclass.getDeclaredFields();
		for (int i = 1; i < fd.length; i++) {
			fd[i].setAccessible(true);
			String fieldname = fd[i].getName();
			sql.append(fieldname + "=" + "?,");
		}
		sql.deleteCharAt(sql.lastIndexOf(","));
		Object valzero;
		try {
			fd[0].setAccessible(true);
			valzero = fd[0].get(o);
			sql.append(" where " + fd[0].getName() + "='" + valzero + "'");
			System.out.println(sql);
			PreparedStatement ps = con.prepareStatement(sql.toString());
			for (int i = 1; i < fd.length; i++) {
				fd[i].setAccessible(true);
				ps.setObject(i, fd[i].get(o));
			}

			ps.executeUpdate();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void insert(T o) {
		StringBuffer sql = new StringBuffer("insert into ").append(tablename).append(" (");
		Field[] fd = tclass.getDeclaredFields();
		for (Field field : fd) {
			try {
				field.setAccessible(true);
				if(field.getAnnotation(Manytoone.class)==null&&field.getAnnotation(Onetomany.class)==null)
				sql.append(field.getName()+",");
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		sql.deleteCharAt(sql.lastIndexOf(","));
		sql.append(") values(");
		for (Field field : fd) {
			try {
				field.setAccessible(true);
				if(field.getAnnotation(Manytoone.class)==null&&field.getAnnotation(Onetomany.class)==null)
				sql.append("?,");
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		sql.deleteCharAt(sql.lastIndexOf(","));
		sql.append(")");
		System.out.println(sql.toString());
		try {
			PreparedStatement pst = con.prepareStatement(sql.toString());
			for (int i = 0; i < fd.length; i++) {
				fd[i].setAccessible(true);
				if(fd[i].getAnnotation(Manytoone.class)==null&&fd[i].getAnnotation(Onetomany.class)==null)
				{
					System.out.println(fd[i].get(o));
					pst.setObject(i + 1, fd[i].get(o));
				}
			}
			pst.executeUpdate();
		} catch (SQLException | IllegalArgumentException | IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public void outFields() {
		Field[] fd = tclass.getDeclaredFields();
		for (Field f : fd) {
			System.out.println(f.getName() + "  " + f.getType().getSimpleName());
		}
	}
}